package com.xpec.c4.codec.h2o;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class CodedInfo {
	/**
	 * 字符串用的编码
	 */
	public static final String CHARSET_NAME = "UTF-8";

	/**
	 * 缓存反射数据 key：对象类型<br>
	 * value：包含的变量
	 */
	public static Map<Class<?>, Field[]> fieldsMapping = new HashMap<Class<?>, Field[]>();

	public static List<Class<?>> supportedJDKClass = new ArrayList<Class<?>>();

	/**
	 * 序号<-->class
	 */
	public static Map<Integer, Class<?>> ingeterClassMapping = new HashMap<Integer, Class<?>>();
	/**
	 * class<-->序号
	 */
	public static Map<Class<?>, Integer> classIngeterMapping = new HashMap<Class<?>, Integer>();

	/**
	 * 类型标识
	 */
	public static final byte TYPE_NULL = 0;
	public static final byte TYPE_BOOLEAN = 1;
	public static final byte TYPE_BYTE = 2;
	public static final byte TYPE_CHAR = 3;
	public static final byte TYPE_SHORT = 4;
	public static final byte TYPE_INT = 5;
	public static final byte TYPE_LONG = 6;
	public static final byte TYPE_FLOAT = 7;
	public static final byte TYPE_DOUBLE = 8;
	public static final byte TYPE_STRING = 9;
	public static final byte TYPE_ENUM = 10;
	public static final byte TYPE_ARRAY = 11;
	public static final byte TYPE_COLLECTION = 12;
	public static final byte TYPE_MAP = 13;
	public static final byte TYPE_CUSTOM = 14;
	public static final byte TYPE_BIGDECIMAL = 15;
	
	/**
	 * 所占位数
	 */
	public static final int TYPE_BITS = 4;

	/**
	 * 类型标识<-->class
	 */
	public static Map<Integer, Class<?>> classTypeMapping = new HashMap<Integer, Class<?>>();
	public static Map<Class<?>, Integer> typeClassMapping = new HashMap<Class<?>, Integer>();

	static {
		supportedJDKClass.add(boolean.class);
		supportedJDKClass.add(byte.class);
		supportedJDKClass.add(char.class);
		supportedJDKClass.add(short.class);
		supportedJDKClass.add(int.class);
		supportedJDKClass.add(long.class);
		supportedJDKClass.add(float.class);
		supportedJDKClass.add(double.class);

		supportedJDKClass.add(Boolean.class);
		supportedJDKClass.add(Byte.class);
		supportedJDKClass.add(Character.class);
		supportedJDKClass.add(Short.class);
		supportedJDKClass.add(Integer.class);
		supportedJDKClass.add(Long.class);
		supportedJDKClass.add(Float.class);
		supportedJDKClass.add(Double.class);

		supportedJDKClass.add(String.class);
		supportedJDKClass.add(BigDecimal.class);
		// /////////
		classTypeMapping.put((int) TYPE_NULL, null);
		classTypeMapping.put((int) TYPE_BOOLEAN, Boolean.class);
		classTypeMapping.put((int) TYPE_BYTE, Byte.class);
		classTypeMapping.put((int) TYPE_CHAR, Character.class);
		classTypeMapping.put((int) TYPE_SHORT, Short.class);
		classTypeMapping.put((int) TYPE_INT, Integer.class);
		classTypeMapping.put((int) TYPE_LONG, Long.class);
		classTypeMapping.put((int) TYPE_FLOAT, Float.class);
		classTypeMapping.put((int) TYPE_DOUBLE, Double.class);
		classTypeMapping.put((int) TYPE_STRING, String.class);
		classTypeMapping.put((int) TYPE_ENUM, Enum.class);
		classTypeMapping.put((int) TYPE_BIGDECIMAL, BigDecimal.class);
		
		for (Integer key : classTypeMapping.keySet()) {
			typeClassMapping.put(classTypeMapping.get(key), key);
		}
	}

	private CodedInfo() {
	}

	public static Class<?> primitiveToObject(Class<?> clz) {
		if (clz == boolean.class) {
			return Boolean.class;
		} else if (clz == byte.class) {
			return Byte.class;
		} else if (clz == char.class) {
			return Character.class;
		} else if (clz == short.class) {
			return Short.class;
		} else if (clz == int.class) {
			return Integer.class;
		} else if (clz == long.class) {
			return Long.class;
		} else if (clz == float.class) {
			return Float.class;
		} else if (clz == double.class) {
			return Double.class;
		} else {
			return clz;
		}
	}

	public static Class<?> objectToPrimitive(Class<?> clz) {
		if (clz == Boolean.class) {
			return boolean.class;
		} else if (clz == Byte.class) {
			return byte.class;
		} else if (clz == Character.class) {
			return char.class;
		} else if (clz == Short.class) {
			return short.class;
		} else if (clz == Integer.class) {
			return int.class;
		} else if (clz == Long.class) {
			return long.class;
		} else if (clz == Float.class) {
			return float.class;
		} else if (clz == Double.class) {
			return double.class;
		} else {
			return clz;
		}
	}

	/**
	 * 注册class，自动分配对应号码
	 * 
	 * @param type
	 * @throws Exception
	 */
	public static void registClass(Class<?> clz) throws Exception {
		registClass(clz,ingeterClassMapping.size());
	}
	
	/**
	 * 注册server内部用到的类用
	 *
	 *  @param clz
	 *  @throws Exception
	 */
//	public static void registServerClass(Class<?> clz) throws Exception {
//		registClass(clz,serverClzNum++);
//	}
	
	/**
	 * 若server与client之间用到类超过1000，则需要重新指定数值
	 * 临时用的，为了account server不用经常发布。
	 */
//	private static int serverClzNum = 1000;
	
	/**
	 * 注册类
	 *
	 *  @param clz
	 *  @param num
	 *  @throws Exception
	 */
	public static void registClass(Class<?> clz, int num) throws Exception {
		if(ingeterClassMapping.containsKey(num))
		{
			System.out.println("num repeat : " + clz.getName());
			//throw new Exception("num repeat : " + clz.getName());
		}
		
		if (clz.isArray() || clz.isEnum() || supportedJDKClass.contains(clz)) {
			throw new Exception("can not be registered class : " + clz.getName());
		}

		if (classIngeterMapping.containsKey(clz)) {
			System.out.println("num repeat : " + clz.getName());
			//throw new Exception("repeat register class : " + clz.getName());
		}

		ingeterClassMapping.put(num, clz);
		classIngeterMapping.put(clz, num);
	}

	/**
	 * 根据序号获得类
	 * 
	 * @param number
	 * @return
	 */
	public static Class<?> getRegisteredClass(int number) {
		return ingeterClassMapping.get(number);
	}

	/**
	 * 根据类获得序号
	 * 
	 * @param number
	 * @return
	 * @throws Exception
	 */
	public static Integer getRegisteredNumber(Class<?> clz) throws Exception {
		Integer num = classIngeterMapping.get(clz);
		if (num == null) {
			throw new Exception("no registered class : " + clz.getName());
		}
		return classIngeterMapping.get(clz);
	}

	private static void checkDefaultConstructor(Class<?> type) throws Exception {
		// 检查是否有默认构造方法
		boolean hasNoParamCons = false;
		for (Constructor<?> constructor : type.getDeclaredConstructors()) {
			if (constructor.getParameterTypes().length == 0) {
				hasNoParamCons = true;
				break;
			}
		}
		if (!hasNoParamCons) {
			throw new Exception("The message class must define no parameter constructor, class:" + type);
		}
	}

	public static void registerFields() throws Exception {
		for (Class clz : ingeterClassMapping.values()) {
			registerFields(clz);
		}
	}

	private static void registerFields(Class<?> type) throws Exception {
		if (fieldsMapping.containsKey(type)) {
			return;
		}

		if (type.isArray()) {
			// registerFields(type.getComponentType());
		} else if (type.isEnum()) {

		} else if (supportedJDKClass.contains(type)) {

		} else if (List.class.isAssignableFrom(type)) {

		} else if (Map.class.isAssignableFrom(type)) {

		} else if (classIngeterMapping.containsKey(type)) {
			// checkDefaultConstructor(type);

			ArrayList<Field> fieldlist = new ArrayList<Field>();
			Class<?> superClz = type;
			while (classIngeterMapping.containsKey(superClz)) {
				// 去掉static和final的
				for (Field field : superClz.getDeclaredFields()) {
					if (!Modifier.isFinal(field.getModifiers()) && !Modifier.isStatic(field.getModifiers())
							&& !Modifier.isTransient(field.getModifiers())) {
						fieldlist.add(field);
					}
				}
				superClz = superClz.getSuperclass();
			}

			Field[] fields = fieldlist.toArray(new Field[fieldlist.size()]);

			// do short
			sort(fields);

			fieldsMapping.put(type, fields);

			for (int i = 0; i < fields.length; i++) {
				fields[i].setAccessible(true);

				Class<?> fieldType = fields[i].getType();

				if (supportedJDKClass.contains(fieldType)) {
					continue;
				}

				if (fieldType.isArray()) {
					registerFields(fieldType.getComponentType());
				} else if (fieldType.isEnum()) {
					registerFields(fieldType);
				} else if (Collection.class.isAssignableFrom(fieldType)) {
					// Type gtype = fields[i].getGenericType();
					// if (gtype instanceof ParameterizedType) {
					// ParameterizedType ptype = (ParameterizedType) gtype;
					// Type[] actuals = ptype.getActualTypeArguments();
					// if (actuals != null && actuals.length == 1) {
					// // String name = ((Class) actuals[0]).getName();
					// // fieldType = Class.forName(name);
					// Class<?> aclz = (Class<?>) actuals[0];
					// registerFields(aclz);
					// continue;
					// }
					// }
					//
					// throw new Exception("collection must define type !!!");
				} else if (Map.class.isAssignableFrom(fieldType)) {
					// Type gtype = fields[i].getGenericType();
					// if (gtype instanceof ParameterizedType) {
					// ParameterizedType ptype = (ParameterizedType) gtype;
					// Type[] actuals = ptype.getActualTypeArguments();
					// if (actuals != null && actuals.length == 2) {
					// Class<?> clzkey = (Class<?>) actuals[0];
					// Class<?> clzvalue = (Class<?>) actuals[1];
					// registerFields(clzkey);
					// registerFields(clzvalue);
					// continue;
					// }
					// }
					//
					// throw new Exception("map must define type !!!");
				} else if (classIngeterMapping.containsKey(type)) {
					registerFields(fieldType);
				} else {
					throw new Exception("no define class:" + fields[i].getType().getName());
				}
			}
		} else {
			throw new Exception("no support class : " + type.getName());
		}
	}

	public static Field[] getClassFields(Class<?> clz) throws Exception {
		if (!fieldsMapping.containsKey(clz)) {
			throw new Exception("no inited fields , class:" + clz.getName());
		}
		return fieldsMapping.get(clz);
	}

	/**
	 * 顺序排序
	 * 
	 * @param fields
	 */
	public static void sort(Field[] fields) {
		Arrays.sort(fields, new Comparator<Object>() {
			@Override
			public int compare(Object o1, Object o2) {
				Field f1 = (Field) o1;
				Field f2 = (Field) o2;

				return f1.getName().compareTo(f2.getName());
			}
		});
	}

	public static int getType(Class<?> type) throws Exception {
		if (type == String.class) {
			return CodedInfo.TYPE_STRING;
		} else if (type == Boolean.class || type == boolean.class) {
			return CodedInfo.TYPE_BOOLEAN;
		} else if (type == Byte.class || type == byte.class) {
			return CodedInfo.TYPE_BYTE;
		} else if (type == Character.class || type == char.class) {
			return CodedInfo.TYPE_CHAR;
		} else if (type == Short.class || type == short.class) {
			return CodedInfo.TYPE_SHORT;
		} else if (type == Integer.class || type == int.class) {
			return CodedInfo.TYPE_INT;
		} else if (type == Long.class || type == long.class) {
			return CodedInfo.TYPE_LONG;
		} else if (type == Float.class || type == float.class) {
			return CodedInfo.TYPE_FLOAT;
		} else if (type == Double.class || type == double.class) {
			return CodedInfo.TYPE_DOUBLE;
		} else if (type.isEnum()) {
			return CodedInfo.TYPE_ENUM;
		} else if (type.isArray()) {
			return CodedInfo.TYPE_ARRAY;
		} else if (Collection.class.isAssignableFrom(type)) {
			return CodedInfo.TYPE_COLLECTION;
		} else if (Map.class.isAssignableFrom(type)) {
			return CodedInfo.TYPE_MAP;
		} else if (CodedInfo.classIngeterMapping.containsKey(type)) {
			return CodedInfo.TYPE_CUSTOM;
		} else {
			throw new Exception("unknown type :" + type.getName());
		}
	}
}
